package com.h2cloud.aspect.Lock;


import com.h2cloud.aspect.BaseAspect;
import com.h2cloud.depends.GetDistributedLockException;
import com.h2cloud.depends.RedisClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.SocketException;


/***
 * 分布式锁拦截器
 * @author xp7
 */
//@Order(3)
@Slf4j
@Aspect
@Component
public class LockAspect extends BaseAspect {

    private static final ThreadLocal<String> perFixKey = new ThreadLocal<>();

    @Autowired
    private RedisClient redisClient;


    @Pointcut("@annotation(com.h2cloud.aspect.Lock.Lock)")
    public void methodPointCut() {

    }

    @Before("methodPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        Lock cacheable = getAnnotation(joinPoint, Lock.class);
        if (cacheable != null) {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = getArgStr(joinPoint.getArgs(), cacheable.paramName(), joinPoint.getSignature());
            int expire = cacheable.expire();
            int cycle = cacheable.cycle();
            int cycleTime = 0;
            String exceptionDesc = cacheable.exceptionDesc();

            String key = getCacheKey(cacheable.key(), targetName, methodName, arguments);
            log.info("aop doBefore, lockKey:{}", key);
            while (true) {
                String lockRequestId;
                try {
                    lockRequestId = getLockRequestId();
                } catch (SocketException e) {
                    throw new GetDistributedLockException("获取lockRequestId异常:" + key, e);
                }
                if (!redisClient.tryGetDistributedLock(key, lockRequestId, expire)) {
                    if (cycleTime >= cycle) {
                        throw new RuntimeException(exceptionDesc);
                    }
                    log.info("log存储对象{}已经被锁定，开始进行第{}次自旋等待。", key, cycleTime);
                    cycleTime++;
                    try {
                        //间隔1秒重试
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new GetDistributedLockException("自旋等待获取分布式锁异常:" + key, e);
                    }
                } else {
                    log.info("log存储对象{}获取分布式锁成功", key);
                    perFixKey.set(key);
                    break;
                }
            }
        }
    }

    @After("methodPointCut()")
    public void doAfter(JoinPoint joinPoint) throws Throwable {
        Lock cacheable = getAnnotation(joinPoint, Lock.class);
        String key = perFixKey.get();
        log.info("aop doAfter, lockKey:{}", key);
        if (cacheable != null && StringUtils.isNotBlank(key)) {
            try {
                if (redisClient.releaseDistributedLock(key, getLockRequestId())) {
                    log.info("log成功释放业务对象{}的redis锁", key);
                } else {
                    log.error("log释放业务对象{}的redis锁失败", key);
                }
            } catch (SocketException e) {
                log.error("log释放业务对象" + key + "的redis锁失败", e);
            }
            perFixKey.set(null);
        }

    }


}
